﻿/*
 * 作者：新生命开发团队（http://www.newlifex.com/）
 * 
 * 版权：版权所有 (C) 新生命开发团队 2002-2014
 * 
 * 修改：海洋饼干（cuteant@outlook.com）
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using CuteAnt.Log;

namespace CuteAnt.Serialization
{
	/// <summary>二进制写入器</summary>
	/// <remarks>
	/// 序列化框架的核心思想：基本类型直接写入，自定义类型反射得到成员，逐层递归写入！详见<see cref="IReaderWriter" />
	///
	/// 二进制序列化，并不仅仅是为了序列化一个对象那么简单，它最初的目标是实现一个高度可自定义的序列化组件，后来提升为以序列化各种协议为重点。
	/// 理论上，只要用实体类实现了各种协议（文件格式），那么它就能只用一个Read/Write实现协议实体对象与二进制数据流之间的映射。
	///
	/// Decimal转为四个Int32整数然后编码输出；
	/// DateTime转为Int64然后编码输出。
	/// </remarks>
	/// <example>
	/// 标准用法：
	/// <code>
	/// var writer = new BinaryWriterX();
	/// writer.Stream = stream;
	/// writer.WriteObject(entity);
	/// </code>
	/// </example>
	public class BinaryWriterX : WriterBase<BinarySettings>
	{
		#region 属性

		private BinaryWriter _Writer;

		/// <summary>写入器</summary>
		public BinaryWriter Writer
		{
			get { return _Writer ?? (_Writer = new BinaryWriter(Stream, Settings.Encoding)); }
			set
			{
				_Writer = value;
				if (_Writer != null && Stream != _Writer.BaseStream) Stream = _Writer.BaseStream;
			}
		}

		/// <summary>数据流。更改数据流后，重置Writer为空，以使用新的数据流</summary>
		public override Stream Stream
		{
			get { return base.Stream; }
			set
			{
				if (base.Stream != value) _Writer = null;
				base.Stream = value;
			}
		}

		#endregion

		#region 构造

		/// <summary>实例化一个二进制写入器</summary>
		public BinaryWriterX()
		{
		}

		/// <summary>实例化一个二进制写入器</summary>
		/// <param name="stream"></param>
		public BinaryWriterX(Stream stream)
			: this()
		{
			Stream = stream;
		}

		#endregion

		#region 字节

		/// <summary>写入字节</summary>
		/// <param name="value">数值</param>
		public override void Write(byte value)
		{
			SetDebugIndent();

			Writer.Write(value);

			AutoFlush();
		}

		/// <summary>将字节数组部分写入当前流。</summary>
		/// <param name="buffer">包含要写入的数据的字节数组。</param>
		/// <param name="index">buffer 中开始写入的起始点。</param>
		/// <param name="count">要写入的字节数。</param>
		public override void Write(byte[] buffer, int index, int count)
		{
			if (buffer == null || buffer.Length < 1 || count <= 0 || index >= buffer.Length) return;

			SetDebugIndent();

			Writer.Write(buffer, index, count);

			AutoFlush();
		}

		/// <summary>判断字节顺序</summary>
		/// <param name="buffer">缓冲区</param>
		private void WriteIntBytes(byte[] buffer)
		{
			if (buffer == null || buffer.Length < 1) return;

			// 如果不是小端字节顺序，则倒序
			if (!Settings.IsLittleEndian) Array.Reverse(buffer);

			Write(buffer, 0, buffer.Length);
		}

		#endregion

		#region 整数

		/// <summary>将 2 字节有符号整数写入当前流，并将流的位置提升 2 个字节。</summary>
		/// <param name="value">要写入的 2 字节有符号整数。</param>
		public override void Write(short value)
		{
			if (Settings.EncodeInt)
				WriteEncoded(value);
			else
				WriteIntBytes(BitConverter.GetBytes(value));
		}

		/// <summary>将 4 字节有符号整数写入当前流，并将流的位置提升 4 个字节。</summary>
		/// <param name="value">要写入的 4 字节有符号整数。</param>
		public override void Write(int value)
		{
			if (Settings.EncodeInt)
				WriteEncoded(value);
			else
				WriteIntBytes(BitConverter.GetBytes(value));
		}

		/// <summary>将 8 字节有符号整数写入当前流，并将流的位置提升 8 个字节。</summary>
		/// <param name="value">要写入的 8 字节有符号整数。</param>
		public override void Write(long value)
		{
			if (Settings.EncodeInt)
				WriteEncoded(value);
			else
				WriteIntBytes(BitConverter.GetBytes(value));
		}

		#endregion

		#region 7位压缩编码整数

		/// <summary>
		/// 以7位压缩格式写入32位整数，小于7位用1个字节，小于14位用2个字节。
		/// 由每次写入的一个字节的第一位标记后面的字节是否还是当前数据，所以每个字节实际可利用存储空间只有后7位。
		/// </summary>
		/// <param name="value">数值</param>
		/// <returns>实际写入字节数</returns>
		public Int32 WriteEncoded(Int16 value)
		{
			List<Byte> list = new List<Byte>();

			Int32 count = 1;
			UInt16 num = (UInt16)value;
			while (num >= 0x80)
			{
				list.Add((byte)(num | 0x80));
				num = (UInt16)(num >> 7);

				count++;
			}
			list.Add((byte)num);

			Write(list.ToArray(), 0, list.Count);

			return count;
		}

		/// <summary>
		/// 以7位压缩格式写入32位整数，小于7位用1个字节，小于14位用2个字节。
		/// 由每次写入的一个字节的第一位标记后面的字节是否还是当前数据，所以每个字节实际可利用存储空间只有后7位。
		/// </summary>
		/// <param name="value">数值</param>
		/// <returns>实际写入字节数</returns>
		public Int32 WriteEncoded(Int32 value)
		{
			List<Byte> list = new List<Byte>();

			Int32 count = 1;
			UInt32 num = (UInt32)value;
			while (num >= 0x80)
			{
				list.Add((byte)(num | 0x80));
				num = num >> 7;

				count++;
			}
			list.Add((byte)num);

			Write(list.ToArray(), 0, list.Count);

			return count;
		}

		/// <summary>
		/// 以7位压缩格式写入64位整数，小于7位用1个字节，小于14位用2个字节。
		/// 由每次写入的一个字节的第一位标记后面的字节是否还是当前数据，所以每个字节实际可利用存储空间只有后7位。
		/// </summary>
		/// <param name="value">数值</param>
		/// <returns>实际写入字节数</returns>
		public Int32 WriteEncoded(Int64 value)
		{
			List<Byte> list = new List<Byte>();

			Int32 count = 1;
			UInt64 num = (UInt64)value;
			while (num >= 0x80)
			{
				list.Add((byte)(num | 0x80));
				num = num >> 7;

				count++;
			}
			list.Add((byte)num);

			Write(list.ToArray(), 0, list.Count);

			return count;
		}

		/// <summary>获取整数编码后所占字节数</summary>
		/// <param name="value">数值</param>
		/// <returns></returns>
		public static Int32 GetEncodedIntSize(Int64 value)
		{
			Int32 count = 1;
			UInt64 num = (UInt64)value;
			while (num >= 0x80)
			{
				num = num >> 7;

				count++;
			}

			return count;
		}

		#endregion

		#region 扩展处理类型

		/// <summary>写入Type</summary>
		/// <param name="value">数值</param>
		protected override void OnWriteType(Type value)
		{
			if (Settings.SplitComplexType)
			{
				if (value.IsArray)
				{
					// 数组类型
					WriteTypeKind(BinarySettings.TypeKinds.Array);

					// 数组维数
					Write(value.GetArrayRank());

					// 数据元素类型
					Write(value.GetElementType());
					return;
				}

				if (value.IsNested)
				{
					// 内嵌类型
					WriteTypeKind(BinarySettings.TypeKinds.Nested);

					// 声明类
					Write(value.DeclaringType);

					// 本类类名
					Write(value.Name);
					return;
				}

				// 特殊处理泛型，把泛型类型和泛型参数拆分开来，充分利用对象引用以及FullName
				if (value.IsGenericType && !value.IsGenericTypeDefinition)
				{
					// 泛型类型
					WriteTypeKind(BinarySettings.TypeKinds.Generic);

					Write(value.GetGenericTypeDefinition());
					Type[] ts = value.GetGenericArguments();
					if (ts != null && ts.Length > 0)
					{
						// 不需要泛型参数个数，因为读取时能从GetGenericArguments得知个数
						//Write(ts.Length);
						foreach (Type type in ts)
						{
							Write(type);
						}
					}

					//else
					//{
					//    Write(0);
					//}
					return;
				}

				// 普通类型
				WriteTypeKind(BinarySettings.TypeKinds.Normal);
			}

			base.OnWriteType(value);
		}

		private void WriteTypeKind(BinarySettings.TypeKinds kind)
		{
			WriteLog("WriteTypeKind", kind);
			WriteValue((Byte)kind);
		}

		#endregion

		#region 写入对象

		/// <summary>写引用对象</summary>
		/// <param name="value">要写入的对象</param>
		/// <param name="type">要写入的对象类型</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否写入成功</returns>
		protected override bool WriteRefObject(object value, Type type, WriteObjectCallback callback)
		{
			if (value != null) type = value.GetType();

			// WriteType必须增加深度，否则写对象引用时将会受到影响，顶级对象不写对象引用
			if (!Settings.IgnoreType && type != null) Write(type);

			return base.WriteRefObject(value, type, callback);
		}

		/// <summary>写入对象引用。</summary>
		/// <param name="value">对象</param>
		/// <returns>是否写入成功</returns>
		public override bool WriteObjRef(object value)
		{
			if (!Settings.UseObjRef)
			{
				// 不使用对象引用时，所有对象不能为空
				//OnWriteObjRefIndex(value == null ? 0 : 1);
				if (value == null)
				{
					// 如果用了FieldSize，为空也没关系
					if (!HasFieldSize()) throw new Exception("不使用对象引用（Settings.UseObjRef=false）时，所有对象不能为空！");
				}

				//return true;
			}

			return base.WriteObjRef(value);
		}

		#endregion

		#region 自定义对象

		/// <summary>写入对象成员</summary>
		/// <param name="name">成员名字</param>
		/// <param name="value">要写入的对象</param>
		/// <param name="type">要写入的对象类型</param>
		/// <param name="index">成员索引</param>
		/// <param name="callback">处理成员的方法</param>
		/// <returns>是否写入成功</returns>
		protected override bool OnWriteMember(String name, Object value, Type type, int index, WriteObjectCallback callback)
		{
			if (!Settings.IgnoreName) Write(name);

			return base.OnWriteMember(name, value, type, index, callback);
		}

		#endregion

		#region 枚举

		/// <summary>写入枚举数据，复杂类型使用委托方法进行处理</summary>
		/// <param name="value">对象</param>
		/// <param name="type">类型</param>
		/// <param name="callback">使用指定委托方法处理复杂数据</param>
		/// <returns>是否写入成功</returns>
		public override bool WriteEnumerable(IEnumerable value, Type type, WriteObjectCallback callback)
		{
			if (value != null && value.GetType().IsArray && value.GetType().GetArrayRank() > 1)
			{
				Array arr = value as Array;
				List<String> lengths = new List<String>();
				for (int j = 0; j < value.GetType().GetArrayRank(); j++)
				{
					lengths.Add(arr.GetLength(j).ToString());
				}
				WriteLengths(String.Join(",", lengths.ToArray()));
			}

			Int32 count = 0;
			if (value != null)
			{
				if (type.IsArray)
				{
					Array arr = value as Array;
					count = arr.Length;
				}
				else
				{
					List<Object> list = new List<Object>();
					foreach (Object item in value)
					{
						// 加入集合，防止value进行第二次遍历
						list.Add(item);
					}
					count = list.Count;
					value = list;
				}
			}

			if (count == 0)
			{
				// 写入0长度。至此，枚举类型前面就会有两个字节用于标识，一个是是否为空，或者是对象引用，第二个是长度，注意长度为0的枚举类型
				WriteSize(0);
				return true;
			}

			// 写入长度
			WriteSize(count);

			return base.WriteEnumerable(value, type, callback);
		}

		#endregion

		#region 方法

		/// <summary>获取需要序列化的成员（属性或字段）</summary>
		/// <param name="type">指定类型</param>
		/// <param name="value">对象</param>
		/// <returns>需要序列化的成员</returns>
		protected override IObjectMemberInfo[] OnGetMembers(Type type, object value)
		{
			var ms = base.OnGetMembers(type, value);
			if (ms != null && value != null)
			{
				// 遍历成员，寻找FieldSizeAttribute特性，重新设定大小字段的值
				foreach (var item in ms)
				{
					var member = item as ReflectMemberInfo;
					if (member != null)
					{
						// 获取FieldSizeAttribute特性
						var att = AttributeX.GetCustomAttributeX<FieldSizeAttribute>(member.Member, true);
						if (att != null) att.SetReferenceSize(value, member.Member, Settings.Encoding);
					}
				}
			}
			return ms;
		}

		/// <summary>写入大小</summary>
		/// <param name="size"></param>
		protected override void OnWriteSize(Int32 size)
		{
			if (HasFieldSize()) return;

			switch (Settings.SizeFormat)
			{
				case TypeCode.Int16:
					Write((Int16)size);
					break;

				case TypeCode.UInt16:
					WriteEncoded((Int16)size);
					break;

				case TypeCode.Int32:
				case TypeCode.Int64:
				default:
					Write(size);
					break;

				case TypeCode.UInt32:
				case TypeCode.UInt64:
					WriteEncoded(size);
					break;
			}
		}

		private Boolean HasFieldSize()
		{
			var member = CurrentMember as ReflectMemberInfo;
			if (member != null)
			{
				// 获取FieldSizeAttribute特性
				var att = AttributeX.GetCustomAttributeX<FieldSizeAttribute>(member.Member, true);
				if (att != null)
				{
					// 如果指定了固定大小或者引用字段，直接返回
					if (att.Size > 0 || !att.ReferenceName.IsNullOrWhiteSpace()) return true;
				}
			}

			return false;
		}

		/// <summary>刷新缓存中的数据</summary>
		public override void Flush()
		{
			Writer.Flush();

			base.Flush();
		}

		///// <summary>已重载。</summary>
		///// <returns></returns>
		//public override string ToString()
		//{
		//    Byte[] buffer = ToArray();
		//    if (buffer == null || buffer.Length < 1) return base.ToString();

		//    return BitConverter.ToString(buffer);
		//}

		/// <summary>使用跟踪流</summary>
		public override void EnableTraceStream()
		{
			var stream = Stream;
			if (stream == null || stream is TraceStream) return;

			var ts = new TraceStream(stream);
			ts.IsLittleEndian = Settings.IsLittleEndian;
			Stream = ts;
		}

		/// <summary>备份当前环境，用于临时切换数据流等</summary>
		/// <returns>本次备份项集合</returns>
		public override IDictionary<String, Object> Backup()
		{
			var dic = base.Backup();
			dic["Writer"] = Writer;

			return dic;
		}

		/// <summary>恢复最近一次备份</summary>
		/// <returns>本次还原项集合</returns>
		public override IDictionary<String, Object> Restore()
		{
			var dic = base.Restore();
			Writer = dic["Writer"] as BinaryWriter;

			return dic;
		}

		#endregion
	}
}